##############################################################################################
#  Copyright (C) 2007 [F.G. Hattingh]																			#
#																															#
#	 This program is free software; you can redistribute it and/or modify							
#	it under the terms of the GNU General Public License as published by								
#	the Free Software Foundation; either version 3 of the License, or									
#	(at your option) any later version.																			#	
#																															#
#	This program is distributed in the hope that it will be useful,											
#	but WITHOUT ANY WARRANTY; without even the implied warranty of										
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the										
#	GNU General Public License for more details.																#
#																															#
#	You should have received a copy of the GNU General Public License										
#	along with this program; if not, write to the Free Software											#
#	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA							
#																															#
#	Contact me at: hattinghfg@tut.ac.za																	#
#																															#
# This lib will help you write applications that work with the MXIT protocoll
##############################################################################################																												#	


package Main;


use Carp;
use IO::Socket; 
use threads;
use threads::shared;
use strict;
use Mixip::Encrypt;
use Mixip::Roster;
use Mixip::Contact;




#######################
# THE CONSTRUCTOR     #
#######################

sub new 
{
my $class = shift;
my $self  = {};
$self->{SERVER_IP}   = "stream.mxit.co.za"; #The server IP that is going to be connected to
$self->{PORT}    = 9119; #The port that is going to be connected to
$self->{APNAME} = undef; #You applications name
$self->{DEBUG} = 0; #If set to true the lib will display debug information
$self->{NUMBER} = undef; #The number the user used to register in the first place
$self->{PIN} = undef; #The users pin number
$self->{CID} = undef; #The client ID you get with the mxit jad
$self->{ROSTER} = Roster->new(); #The roster to keep track of the users.
$self->{ENCRYPT} = undef; #Encrypt package that handles encryption
bless ($self, $class);
return $self;
}

####################
# GLOBAL VARIABLES #
####################
our $VERSION = "0.0_src";

our $counter = 0; #Used to count stuff
our $packet = ""; #A complete message from server 
our $hex_packet = ""; #The same message in hex 

##Error stuff uses for internal debug
##Comment out in production
#open ERROR,  '>', "error.txt"  or die $!;
#STDERR->fdopen( \*ERROR,  'w' ) or die $!;
 
#The host database is just a hash that holds the hostname eg:27834151622@m1.mxit.co.za as key and the nick as value
our %host_db = ();

#The nick database is just a hash that holds the hostname eg:27834151622@m1.mxit.co.za as Value and the nick as key
our %nick_db :shared = ();

#The Socket that will be used for connection
our $Socket;

#Events
our %events_link = (
        raw_line =>undef, #The raw_line event happens when server recieves a line
        end_read => undef, #When the socket stoped receiving data
        connecting => undef, #Whe the conection is beign created
        start_read => undef, #When the loop that reads data from socket is started
        connected => undef, #When the connection sucseeded
        connect_fail => undef, #When the connection failed
        raw_send =>undef, #When the user sent a raw message to the server
        login_send =>undef, #When the user did a login
        contacts_received =>undef, #When we receive a list of contacts after login
        contact_status_change =>undef, #When a status changes this gets called
        message_received =>undef, #When a new message is received
        contact_invite_received =>undef, #When we recieve a contact invite
    );


#Null byte in hex
use constant NULL_B_HEX => "\x00";
#Null byte 1 in hex
use constant NULL_B1_HEX => "\x01";
#Null byte 2 in hex
use constant NULL_B2_HEX => "\x02";


############################
# SUBS TO CHANGE VARIABLES #
############################

sub self
{
my $self = shift;
print $self;
}

sub encrypt
{
my $self = shift;

	if (scalar(@_)==1) 
	{
	 $self->{ENCRYPT} = shift ;
	}
	if (scalar(@_)==2) 
	{
	 $self->{ENCRYPT} =  Encrypt->new('Pin'=>@_[0],'Cid'=>@_[1]);
	}
	return $self->{ENCRYPT};
} 

sub roster
{
my $self = shift;
	if (@_) 
	{
	 $self->{ROSTER} = shift ;
	}
	return $self->{ROSTER};
}

sub number
{
my $self = shift;
	if (@_) 
	{
	 $self->{NUMBER} = shift ;
	}
	return $self->{NUMBER};
}

sub pin
{
my $self = shift;
	if (@_) 
	{
	 $self->{PIN} = shift ;
	}
	return $self->{PIN};
}

sub cid
{
my $self = shift;
	if (@_) 
	{
	 $self->{CID} = shift ;
	}
	return $self->{CID};

}

sub debug
{
my $self = shift;
	if (@_) 
	{
	 $self->{DEBUG} = shift ;
	}
	return $self->{DEBUG};
}

sub apname
{
my $self = shift;
	if (@_) 
	{
	 $self->{APNAME} = shift ;
	}
	return $self->{APNAME};
	
}

sub port
{
	my $self = shift;
	if (@_) 
	{
	 $self->{PORT} = shift ;
	}
	return $self->{PORT};
}

sub server_ip
{
	my $self = shift;
	if(@_)
	{$self->{SEVER_IP} = shift;
	}
	return $self->{SERVER_IP}
}


##############################################
# SUBS TO HANDLE EVENTS THAT USER CAN DEFINE #
##############################################

# This sub is the common backend to add_handler 
sub _add_generic_handler {
  my ($self, $event, $ref, $rp, $hash_ref, $real_name) = @_;
  my %define = ( "replace" => 0, "before" => 1, "after" => 2 );
  
  unless (@_ >= 3) 
  {
    croak "Not enough arguments to $real_name()";
  }
  unless (ref($ref) eq 'CODE') 
  {
    croak "Second argument of $real_name isn't a coderef";
  }
  
  # Translate REPLACE, BEFORE and AFTER.
  if (not defined $rp) 
  {
    $rp = 0;
  } 
  elsif ($rp =~ /^\D/) 
  {
    $rp = $define{lc $rp} || 0;
  }
    
    $events_link{lc $event} = $ref;

  return 1;
}

# This sub will assign a user's custom function to a particular event which
# this connection might receive.  Same args as above.
sub add_handler {
  my ($self, $event, $ref, $rp) = @_;
  return $self->_add_generic_handler($event, $ref, $rp, $self->{HANDLER}, 'add_handler');
} 


##############################
# SUBS TO HANDLE CONNECTIONS #
##############################

sub disconnect
{
close($Socket);
threads->detach();
}

#Connect to server 
sub connect
{
my $self = shift;
my $port = $self->{PORT};
my $server = $self->{SERVER_IP};
	
#Call the sub the user defined when he wants to know if whe are connecting
&call_event($events_link{connecting},$self);


$Socket = new IO::Socket::INET( PeerAddr => $server, PeerPort => $port, Proto => 'tcp'); 
$Socket or return &call_event($events_link{connect_fail},$self,$Socket);  #die "no socket :$!"; 

$Socket->autoflush(1);

#Call the sub that watches if the sock did connect 
&call_event($events_link{connected},$self,$Socket);

#Start a new thread that will run the socket_reader sub
my $thr = threads->create('socket_reader',$self);

}


##############################
# SUBS THAT READ FROM SOCKET #
##############################

#Reads data from the socket 
sub socket_reader
{
my $self = @_[0];

#Call the sub the user defined when reader is started
&call_event($events_link{start_read},$Socket);


while()
{
#Read on byte at a time
my $buf;	
sysread($Socket, $buf, 1);


#If you find a byte send that byte to the &_top_handler function
&_top_handler($buf,$self);

}


#Call the sub the user defined when reader came to a end 
&call_event($events_link{end_read},$Socket);

}



##################################################
# SUBS THAT RESPOND TO WHAT WAS READ FROM SERVER #
##################################################

# ***This handler will receive the raw bytes from the server and ship out accordingly ***
sub _top_handler
{
	my $self = @_[1];


	my $byte =@_[0];
	my $hex_byte = &ascii_to_hex($byte);
	
	$counter = $counter-1;
	
	if(!$byte eq "")
	{
	#print "$counter Top: $byte , $hex_byte \n";
	}
	
	#Add the bytes to the holders
	$packet = $packet.$byte;
   $hex_packet = $hex_packet.$hex_byte;
   #print  "\n$hex_packet\n";

	#Because whe receive one byte at a time we must read until the Lengh of a message is reached or when we receive a 00 (null) and then 6c (l) that 
	#tell us a new ln is received and the old ln is done
	
	#So the options are:
	#1. Append each byte to the $packet string until we get the lengh: ln=1239\x00 the according to the lengh read the rest of the message send 
	#	 $packet to packet_handler and start over
	
	#2. Read each byte and check each time that a byte is added to $packet if the last two bytes are 00 and 6c if that is the case remove the 6c and send 
	#	 $packet to packet_handler clear $packet and add 6c to packet with the new incoming stuff
	
	#This is the implementation of option 1 feel free to implement option 2 and send a patch to that we can compare what works best remember so long
	#as &packet_handler gets a complete packet all is good
	
	if($hex_packet =~ m/^6c6e3d(.*)00$/)
	{
	#print "\n$packet\n";
	
	$counter = &hex_to_ascii($1); #How much must whe read
	#print "\n$counter\n";
	#Clear the hex packet 
	$hex_packet = "";

	}
	
	if(!$counter)
	{
	#print "End reaced\n";
	#Call the packet hackler function 
	&packet_handler($packet,$self);
	#Clear the packets
	$packet = "";
	$hex_packet = "";
	}
}

#*** Gets the packets and ship then out to functions created to read and work on them *** 
sub packet_handler
{
#print "Handling new packet\n";
my $packet_ = @_[0]; 
my $packet_hex_=&ascii_to_hex($packet_);

my $self = @_[1];


#&proto_dump($packet_hex_);

##Get the type of packet that was received
##Currently using the number after ln=123/x00USING THIS NUMBER/x00

#Contact Invite Received = 51
if($packet_hex_ =~ m/^6c6e3d.*00353100/)
{
&contact_invite_received_handler($packet_);
}

#Contacts received during Login = 1
if($packet_hex_ =~ m/^6c6e3d.*003100/)
{
#print "Contact list received on login\n";
&contact_received_handler($packet_,$self);
}

#Contacts online or ofline or away received  = 3 [39]
if($packet_hex_ =~ m/^6c6e3d.*003300/)
{
&contact_status_change($packet_);
}

#Message received = 9 [39]
if($packet_hex_ =~ m/^6c6e3d.*003900/)
{
&message_received_handler($packet_);
}


#Get the sub for raw_line the user wants called
&call_event($events_link{raw_line},$packet_);
}


# *** A new message was recieved [koos: "This doesnt happen to me a lot so it is a big deal ok"]
sub message_received_handler
{
my $packet_ = @_[0]; 


#Split the string
my @values = split(/${\NULL_B_HEX}/, $packet_); 

my @message =();

for my $item (@values)
{
	#Dont push ln=
	if($item !~ m/^ln=.*/)
	{
		#Dont push the entire ! into @message and replace the /x02 ~ with nothing
		if($item !~ m/${\NULL_B1_HEX}/)
		{
		$item =~ s/${\NULL_B2_HEX}//;
		push(@message,$item);
		}
		
		#Split ! divided part and push into @message
		if($item =~ m/${\NULL_B1_HEX}/)
		{
			my @mes_split = split(/${\NULL_B1_HEX}/, $item); 
			for my $mes_it (@mes_split) 
			{
			push(@message,$mes_it );
			}
		}
	
	}
}

#Create a hash
my %message_h = ('num1',@message[0], 'num2', @message[1], 'host', @message[2], 'num3', @message[3], 'num4', @message[4], 'num5', @message[5], 'num6', @message[6], 'message', @message[7]);


#Help the user out and create a host and message varible
my $host = @message[2];
my $mes = @message[7];

#Call event the user created
&call_event($events_link{message_received},@_[0],$packet_,\@message,\%message_h,$host,$mes); 

}



# *** When we receive a contact invite 
sub contact_invite_received_handler
{
my $packet_ = @_[0]; 


#Store the invite
my @invite = ();

#Split into * sections
my @values = split(/${\NULL_B_HEX}/, $packet_); 

	for my $item (@values)
	{
	#Dont push ln=
	if($item !~ m/^ln=.*/)
	{
		#Split ! divided part and push into @message
		if($item =~ m/${\NULL_B1_HEX}/)
		{
			my @mes_split = split(/${\NULL_B1_HEX}/, $item); 
			for my $mes_it (@mes_split) 
			{
			push(@invite,$mes_it );
			}
		}
	}
	}

#Create a hash
my %invite_h = ('host',@invite[0], 'nick', @invite[1], 'num1', @invite[2], 'num2', @invite[3],'mes',@invite[4]); 

#Append host and nick db
$host_db{@invite[0]} =  @invite[1];
$nick_db{@invite[1]} =  @invite[0];

#Call user defined event 	 
&call_event($events_link{contact_invite_received},@_[0],$packet_,\@invite,\%invite_h); 	
}



# *** Gets called when a new contacts received on login mostly I would guess ***
sub contact_received_handler
{
my $packet_ = @_[0]; 
my $self = @_[1]; 


my @cont_ainer = ();

#Split the string
my @values = split(/${\NULL_B_HEX}/, $packet_);

#Holds the outstanding contct invites
my @cont_out = ();


#Drop unwanted stuff and load a new array for each contact
foreach my $val (@values) {
	 
 #Only work with values ending in ![Any char]	
 if($val =~m/${\NULL_B1_HEX}.$/ )
 {
  #Look at firts character of $val and if this char is not ! then it seems we are dealing with an unresolved invite
  #IOW we need to tel user he has invite. We look for any size off 4 after the split
  my @contacts = split(/${\NULL_B1_HEX}/, $val);
  my $size = @contacts;
  
  if($size == 4)
  {
  #Deal with the outstanding invite
  push(@cont_out,$val);
  }
  else
  {
  push(@cont_ainer,  [@contacts]);
  }
 }
}

#Create a Contact object for each contact and add to the roster
for my $aref (@cont_ainer){
	my $contact = Contact->new('Group'=>@$aref[0],'Host'=>@$aref[1],'Nick'=>@$aref[2],'Num1'=>@$aref[3],'Num2'=>@$aref[4],'Num3'=>@$aref[5]);
	$self->{ROSTER}->add_contact($contact);
}

##Create an array of hash (Note this is not illegal in some countries ;))
my @cont_hash = ();

for my $aref (@cont_ainer){
 my %cont_g_h = ();
 %cont_g_h = ('group',@$aref[0], 'host', @$aref[1], 'nick', @$aref[2], 'num1', @$aref[3], 'num2', @$aref[4], 'num3', @$aref[5]);
 push(@cont_hash, { %cont_g_h });
 
 #Also update the host databse so that user dont need to keep track of what host is linked to what nick
 $host_db{@$aref[1]} = @$aref[2];     
 }
 
 #Reverse the host_db to link the nick to hostname
 %nick_db = reverse %host_db;

#For each contact that was not handled send to contact_invite_received_handler to process
for my $out_con (@cont_out)
{
	&contact_invite_received_handler($out_con);
}

#Call event the user created
&call_event($events_link{contacts_received},@_[0],$packet_,\@cont_ainer,\@cont_hash,\@cont_out); 
}



#*** Handle it when a contact status changes especially on login ***
sub contact_status_change
{
my $packet_ = @_[0]; 

#Replace
#$packet_ = &replace_null($packet_);

#Split out any messages tha was sent while ofline this is between ~
my @core_values = split(/${\NULL_B2_HEX}/, $packet_);



my @value = ();
my @extra_mes =();

#Divide between ofline messages and core values aka what we are indeed looking for.
#We assume that a core value will contain the text ln= since the extra messae was split not
for my $val (@core_values)
{
	if($val=~m/^ln=.*${\NULL_B_HEX}/)
	{
	 $packet_ = $val; #packet is now clean of any extra messages
	}
	else
	{
	 push(@extra_mes,$val);
	}
}

#Split the string
my @values = split(/${\NULL_B_HEX}/, $packet_);

my @contacts_online = ();

#Drop unwanted stuff and load a new array for each contact that is online
foreach my $val (@values) {
 if($val =~m/^.*${\NULL_B1_HEX}/ )
 {
  my @contacts = split(/${\NULL_B1_HEX}/, $val);
  push(@contacts_online,  [@contacts]);
 }
}


my @contacts_online_h =();

#Build an array of hashes containg info about each contact change
for my $aref (@contacts_online){
 my %cont_g_h = ();
 %cont_g_h = ('host', @$aref[1], 'nick', @$aref[2], 'num1', @$aref[3], 'num2', @$aref[4], 'num3', @$aref[5]);
 push(@contacts_online_h, { %cont_g_h });
 }
 

#Send the extra message that was received after login to message_received_handler to be handled like normal message
for my $e_mes (@extra_mes)
{
 &message_received_handler($e_mes);
}

#Call event the user created
&call_event($events_link{contact_status_change},@_[0],$packet_,\@contacts_online,\@contacts_online_h,\@extra_mes); 
}

#Call all the events the user defined when calling the user defined event all the call_event arguments gets returned.
sub call_event
{
my $raw_event =  @_[0];

	#If not ""
 	if($raw_event != "")
 	{
  	&$raw_event(@_);
  	}

}



###################################
# SUBS THAT SENDS STUFF TO SERVER #
###################################

#***Add ln= and Send data over $Socket
sub Send
{
my $packet = shift;
$packet = &mes_len($packet);
$Socket->send($packet);
}

#*** Send a raw message to the server 
sub send_raw
{
my $line = @_[1];

$Socket->send($line);

#Call the sub the user defined when he sent a raw message to the server 
&call_event($events_link{raw_send},@_); 

}

#*** Send a message to someone
sub send_message
{
my $self = @_[0];
my $number = $self->{NUMBER} ;

my $mes = @_[2];
my $nick = @_[1];
my $host = &get_host($self,$nick);

#Create building blocks based on Message send from Wiki
my $id = "id=$number";
my $cm = "cm=10";
my $ms = "ms=$host";

#Build the packet
my $packet =  $id.NULL_B_HEX.$cm.NULL_B_HEX.$ms.NULL_B1_HEX.$mes.NULL_B1_HEX."1";

#Send packet
&Send($packet)

} 

#** Login using encryption
sub login
{
#Call the sub the user defined when he sent a login message to the server 
&call_event($events_link{login_send},@_); 

my $self = @_[0];

my $number = $self->{NUMBER} ;
my $pin = $self->{PIN} ;
my $cid = $self->{CID};
my $encrypt = undef; 

#Must we instantiate encrypt (undef)) or has the user done that already
if(!defined $self->{ENCRYPT})
{
$encrypt = ($self->{ENCRYPT} =  Encrypt->new('Pin'=>$pin,'Cid'=>$cid)); #Instantiate Encrypt and assign to $encrypt
}
else
{
 $encrypt = $self->{ENCRYPT}; 
}

#First encode the pin using Mixip::Encrypt send the pin and cid to constructor
#my $encrypt = Encrypt->new('Pin'=>$pin,'Cid'=>$cid);
#Call enc_pin that returns @ containg the encrypted pin and the changed $cid
($pin,$cid) = $encrypt->enc_pin;


#Create building blocks based on login from Wiki
my $id = "id=$number";
my $cm = "cm=1";
my $ms = "ms=$pin";

#This makes a difference beleive it or not... the E-5.7.2 part 
#With E-5.7.2 The password needs to be encrypted same with: E-5.6.3, 5.6.0, 5.5.0 5.4.1
#We will use E-5.7.2

my $version = "E-5.7.2-J-MicroEmulator-2.0";
my $settings = "ver=5.7.2;loc=en;cat=J;w=176;h=220;c=65536;a=256;feat=522187;cid=0;ctyp=65;dmem=3738;lmem=1;fmem=1048576";
#Dont forget about the $cid
my $mistnum = "522187"; #??
my $t7 = "27";
my $cr = "cr=wipe";


#Build packet
my $packet = $id.NULL_B_HEX.$cm.NULL_B_HEX.$ms.NULL_B1_HEX.$version.NULL_B1_HEX."1".NULL_B1_HEX.$settings.NULL_B1_HEX.$cid.NULL_B1_HEX.$mistnum.NULL_B1_HEX.$t7.NULL_B1_HEX."en".NULL_B_HEX.$cr;

#Send packet
&Send($packet)

}

#Login without using encryption
sub login_old
{
#Call the sub the user defined when he sent a login message to the server 
&call_event($events_link{login_send},@_); 

my $self = @_[0];

my $number = $self->{NUMBER} ;
my $pin = $self->{PIN} ;
my $cid = $self->{CID};



#Create building blocks based on login from Wiki
my $id = "id=$number";
my $cm = "cm=1";
my $ms = "ms=$pin";

#This makes a difference beleive it or not... the E-5.7.2 part 
#With E-5.7.2 The password needs to be encrypted same with: E-5.6.3, 5.6.0, 5.5.0 5.4.1
#With E-5.1.1 Can login but contact invite does not send invite message ect for encryption use function: login()

my $version = "E-5.1.1-M-MicroEmulator-2.0";
my $settings = "dist=E;ver=5.1.1;cat=M;platform=MicroEmulator-2.0;width=176;height=220;cols=65536;maxmsg=10000;features=73;ctypes=1";
#Dont forget about the $cid
my $mistnum = "73"; #??
my $t7 = "27";
my $cr = "cr=";

#Build packet
my $packet = $id.NULL_B_HEX.$cm.NULL_B_HEX.$ms.NULL_B1_HEX.$version.NULL_B1_HEX."1".NULL_B1_HEX.$settings.NULL_B1_HEX.$cid.NULL_B1_HEX.$mistnum.NULL_B1_HEX.$t7.NULL_B_HEX.$cr;

&Send($packet)
}

sub logout
{
my $self = @_[0];
my $number = $self->{NUMBER} ;


#Create building blocks based on logout from Wiki
my $id = "id=$number";
my $cm = "cm=2";
my $ms = "ms=0";

#Build packet
my $packet =$id.NULL_B_HEX.$cm.NULL_B_HEX.$ms ;

&Send($packet)
}

# *** Invite a contact
sub invite_contact
{
my $self = @_[0];
my $con_num = @_[1];
my $con_nick = @_[2];
my $number = $self->{NUMBER} ;


#Create building blocks based on Invite contact from Wiki
my $id = "id=$number";
my $cm = "cm=6";
my $ms = "ms=".NULL_B1_HEX.$con_num.NULL_B1_HEX.$con_nick.NULL_B1_HEX."0";

#Build packet
my $packet = $id.NULL_B_HEX.$cm.NULL_B_HEX.$ms ;

#Send the packet
&Send($packet);
}


# *** Delete a contact
sub delete_contact
{
&contact_abstract(@_[0],@_[1],"8");
}

#*** Reject a contact invite.
sub reject_contact_inv
{
&contact_abstract(@_[0],@_[1],"55");
}

#*** Accept a contact invite.
sub accept_contact_inv
{
&contact_abstract(@_[0],@_[1],"52");
}

#** Does a lot of contact functions
sub contact_abstract
{
#Some of the contact operations are the same in build. The only thing that changes is the cm=??.
#This function receives the nick, and cm and based on the cm does what it needs.
#cm = 55 [Reject contact invite]
#cm = 52 [Accept contact invite]
#cm = 8 [Delete contact]

my $self = @_[0];
my $nic = @_[1];
my $cm_num = @_[2];

my $host = &get_host($self,$nic);
my $number = $self->{NUMBER} ;


#Create building blocks
my $id = "id=$number";
my $cm = "cm=$cm_num";
my $ms = "ms=$host";

#Build packet
my $packet = $id.NULL_B_HEX.$cm.NULL_B_HEX.$ms ;

#When accepting contact invite add the nick to $packet
if($cm_num eq "52")
{
$packet = $packet.NULL_B1_HEX."".NULL_B1_HEX.$nic;
}

&Send($packet);
}



###################################################################
# UTILITY SUBS THAT HELP OUT WITH THIS AND THAT INSIDE THE MODULE #
###################################################################



sub hex_to_ascii
{
# Convert hex to ascii 
#Thanks John Winger: http://www.planet-source-code.com/URLSEO/vb/scripts/ShowCode!asp/txtCodeId!627/lngWid!6/anyname.htm
(my $str = shift) =~ s/([a-fA-F0-9]{2})/chr(hex $1)/eg;
return $str;
}

sub ascii_to_hex
{
#Convert ascii to hex 
#Thanks John Winger: http://www.planet-source-code.com/URLSEO/vb/scripts/ShowCode!asp/txtCodeId!627/lngWid!6/anyname.htm
(my $str = shift) =~ s/(.|\n)/sprintf("%02lx", ord $1)/eg;
return $str;
}

sub proto_dump
{
#Give the hex hump of the protocol to this function to print the asci then hex then ascii next to hex
my $display = $_[0];

print "\nHEX: $display\n"; 

my $ascii = &hex_to_ascii($display);

print "\nASCII: $ascii\n"; 

	while ($ascii =~ /(.)/g) 
	{
	 my $hexc = &ascii_to_hex($1);
	 print "$1 $hexc\n ";
	}
}

sub mes_len
{
#Returns the lengh of the message with ln=
my $message =  $_[0];
my $lengh = length($message) ;

return "ln=".$lengh.NULL_B_HEX.$message;
}



###################################################
# UTILITY SUBS THAT THE USER MAY FIND USEFULL     #
###################################################
sub get_nick
{
my $self = @_[0];
my $host = @_[1]; 

return $host_db{$host};
}

sub get_host
{
my $self = @_[0];
my $nick = @_[1];  

return $nick_db{$nick} ;
}

sub test
{

&proto_dump("6c6e3d3234300069643d323738333431353136323300636d3d31006d733d6c412f705a36476a4f6f317147783033335847776a773d3d01452d352e372e322d4a2d4d6963726f456d756c61746f722d322e300131017665723d352e372e323b6c6f633d656e3b6361743d4a3b773d3137363b683d3232303b633d36353533363b613d3235363b666561743d3532323138373b6369643d303b637479703d36353b646d656d3d333733383b6c6d656d3d313b666d656d3d313034383537360133363330393743342d313530422d343639332d383430432d3837433031324546433839310135323231383701323701656e0063723d77697065");

}

sub get_jad_cid
{

#Use this sub to get the CID from a jad file that the user downloaded
my $path = $_[1];

open (JAD, $path);

while(<JAD>)
{
	#Look for the c: xxxx entry
	if(/^c: (.*)$/)
	{
	return $1;
	}
}
close (JAD);

}

1;
